using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Text;
using System.Drawing;
namespace sla
{
    class Signal
    {
#region Signal class private members
        ArrayList signalDB;
        PictureBox signalDrawingArea; 
        Color signalColor;
        float signalWidth;
        public Color gridColor;
        public float gridWidth;
        string signalName;
        public Color backgroundColor;
        static int verticalGridLinesCount;
        Font signalNameFont;
        SolidBrush SignalNameBrush;
        static float timePerDivisionInMicroSeconds = 10f;
        static float currentTimeStamp;
        static float horizontalDivisionSize;
        static int drawingAreaWidth;

#endregion 

#region Signal calss properties
        public string SignalName
        {
            get { return signalName; }
            set { signalName = value; }
        }
        public Color SignalColor
        {
            get { return signalColor; }
            set { signalColor = value; } 
        }

        public float SignalWidth
        {
            get { return signalWidth; }
            set { signalWidth = value; } 
        }
        public static float TimePerDivisionInMicroSeconds
        {
            set
            {
                timePerDivisionInMicroSeconds = value;
            }
            get
            {
                return timePerDivisionInMicroSeconds;
            }
        }
        public static float CurrentTimeStamp
        {
            set
            {
                currentTimeStamp = value;
            }
            get
            {
                return currentTimeStamp;
            }
        }

#endregion
 

#region signal class public methods

        public Signal(MainForm mainForm, string name, Rectangle rect)
        {
            signalDB = new ArrayList();
            signalName = name;
            signalColor = Color.GreenYellow;
            signalWidth = 1.0f;
            //TODO: refactor gridColor to gridLineColor, same for signalColor
            // Creating the data base of a single signal which holds the value of this signal during all the time period (all time stamps)
            gridColor = Color.DarkGray;
            gridWidth = 0.5f;
            backgroundColor = Color.Black;
            Signal.verticalGridLinesCount = 10;
            signalNameFont = new Font("Monospace", 10);
            SignalNameBrush = new SolidBrush(Color.Blue);
            currentTimeStamp = 0;
            Signal.drawingAreaWidth = rect.Width;

#if false  
            signalDB.Add(new SignalInfo(0, 1));
            signalDB.Add(new SignalInfo(10, 0));


            signalDB.Add(new SignalInfo(15, 1));
          
            signalDB.Add(new SignalInfo(20, 0));


            signalDB.Add(new SignalInfo(25, 1));
            signalDB.Add(new SignalInfo(30, 0));
            signalDB.Add(new SignalInfo(35, 1));
            signalDB.Add(new SignalInfo(40, 0));
            signalDB.Add(new SignalInfo(50, 1));
            signalDB.Add(new SignalInfo(60, 0));
            signalDB.Add(new SignalInfo(70, 1));
            signalDB.Add(new SignalInfo(80, 0));
            signalDB.Add(new SignalInfo(100, 1));
            signalDB.Add(new SignalInfo(110, 0));
            signalDB.Add(new SignalInfo(120, 1));
            signalDB.Add(new SignalInfo(130, 0));
            signalDB.Add(new SignalInfo(140, 1));
            signalDB.Add(new SignalInfo(145, 0));
            signalDB.Add(new SignalInfo(150, 1));
            signalDB.Add(new SignalInfo(170, 0));
            signalDB.Add(new SignalInfo(190, 1));
            
#endif
            #region initializing the signal drawing area (picture box) -> the following steps was copied from a trial of windows generated pic box
            // Creating the picture box which represents the drawing area for a single signal
            signalDrawingArea = new System.Windows.Forms.PictureBox();

            // Initialzing the location of the the pic. box
            signalDrawingArea.Location = new System.Drawing.Point(rect.Location.X, rect.Location.Y);
            ((System.ComponentModel.ISupportInitialize)(signalDrawingArea)).BeginInit();
            
            // Suspending the mainform to avoid flikering during setting up the signal pic box.
            mainForm.SuspendLayout();

            // Adjusting the parameters of the pic box
            signalDrawingArea.Name = signalName;
            signalDrawingArea.Size = new System.Drawing.Size(rect.Width, rect.Height);
            signalDrawingArea.TabIndex = 0;
            signalDrawingArea.TabStop = false;
            
            // Adding this signal pic box to the main form control so that the main form automatically reftesh this box with any form refresh 
            mainForm.addToSignalsPannel(signalDrawingArea);

            // Adding the delegate of the signalDrawingArea_Paint to the paint event of the pic box.  
            signalDrawingArea.Paint += new System.Windows.Forms.PaintEventHandler(signalDrawingArea_Paint); 
            ((System.ComponentModel.ISupportInitialize)(signalDrawingArea)).EndInit();

            // Resuming mainForm layout...
            mainForm.ResumeLayout(false);
            #endregion
            
            
        }

        // This method draws one signal
        public void signalDrawingArea_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {

            int signalClearance = this.signalDrawingArea.Height / 4;
            Graphics graphics = e.Graphics;
            Pen signalPen = new Pen(this.signalColor, this.signalWidth);
            Pen gridPen = new Pen(this.gridColor, this.gridWidth);
            graphics.Clear(this.backgroundColor);
            graphics.DrawRectangle(gridPen, signalDrawingArea.DisplayRectangle);
            #region Draw Signal Name
            
            graphics.DrawString(this.signalDrawingArea.Name, signalNameFont, SignalNameBrush, new PointF(0, 0));
            #endregion

            #region Draw Grid

            
            float currentVerticalGridLinePosition = Signal.horizontalDivisionSize;
            
            for (int i = 0; i < Signal.verticalGridLinesCount; i++)
            {
                graphics.DrawLine(gridPen, currentVerticalGridLinePosition, 0, currentVerticalGridLinePosition, this.signalDrawingArea.Height);
                currentVerticalGridLinePosition += Signal.horizontalDivisionSize;
            }
            #endregion

            #region Draw Signal

            int firstSignalIndex;
            int lastSignalIndex;

            firstSignalIndex = getFirstSignalIndex(this.signalDB, Signal.currentTimeStamp);
            lastSignalIndex = getLastSignalIndex(this.signalDB, firstSignalIndex);
            for (int i = firstSignalIndex; i < lastSignalIndex; i++)
            {
                drawOneSignalSegment((SignalInfo)signalDB[i], (SignalInfo)signalDB[i + 1], graphics, signalPen, 0 + signalClearance, this.signalDrawingArea.Height - signalClearance);
            }

            #endregion
        }
        public void addNewSignalInfo(SignalInfo newSignalInfo)
        {
            this.signalDB.Add(newSignalInfo);
        }

#endregion
 
#region signal class private methodes
        private int getLastSignalIndex(ArrayList arrayList, int firstSignalIndex)
        {
            int lastSignalIndex = firstSignalIndex;
            float totalTime = (Signal.verticalGridLinesCount - 1) * Signal.timePerDivisionInMicroSeconds;
            while ((lastSignalIndex < (arrayList.Count - 1))&&(((SignalInfo)arrayList[lastSignalIndex]).timeStamp - ((SignalInfo)arrayList[firstSignalIndex]).timeStamp < totalTime))
            {
                lastSignalIndex++;
            }
            return lastSignalIndex;
        }

        private int getFirstSignalIndex(ArrayList arrayList, float currentTimeStamp)
        {
            int index = 0;
            while((index < (arrayList.Count - 1)) && ((SignalInfo)arrayList[index]).timeStamp < currentTimeStamp)
            {
                index++;
            }
            return index;
        }

        // This method draws one segment of the signal 
        private void drawOneSignalSegment(SignalInfo signalInfoCurrent, SignalInfo signalInfoNext,Graphics graphics , Pen pen, float lowSignalValue, float highSignalValue)
        {
            float Y_Position;
            float X_Position1;
            float X_Position2;

            if (signalInfoCurrent.value == 1)
            {
                Y_Position = lowSignalValue;
            }
            else
            {
                Y_Position = highSignalValue;
            }

            X_Position1 = TimeToPosition(signalInfoCurrent.timeStamp - Signal.CurrentTimeStamp);
            X_Position2 = TimeToPosition(signalInfoNext.timeStamp - Signal.CurrentTimeStamp);
            graphics.DrawLine(pen, X_Position1, Y_Position, X_Position2, Y_Position);
            
            if (signalInfoCurrent.value != signalInfoNext.value)
            {
                X_Position1 = TimeToPosition(signalInfoNext.timeStamp - Signal.CurrentTimeStamp);
                
                graphics.DrawLine(pen, X_Position1, lowSignalValue , X_Position1, highSignalValue);
            }
        }

        public static float TimeToPosition(float timeStamp)
        {
            return (((timeStamp - Signal.currentTimeStamp)/Signal.timePerDivisionInMicroSeconds) * horizontalDivisionSize);
        }

#endregion 
    
    
        public void refresh()
        {
            this.signalDrawingArea.Invalidate();
        }



        public static float PositionToTime(float position)
        {
            return (Signal.currentTimeStamp + (position / Signal.horizontalDivisionSize) * Signal.timePerDivisionInMicroSeconds);     
        }

        public static void UpdateDrawingParameters()
        {
            Signal.horizontalDivisionSize = Signal.drawingAreaWidth / Signal.verticalGridLinesCount;
                 
        }

        public static void UpdateCurrentTimeStamp(float deltaTimeStamp)
        {
            if ((Signal.currentTimeStamp + deltaTimeStamp) >= 0)
            {
                Signal.currentTimeStamp += deltaTimeStamp;
            }
        }
    }
}